home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1997 September
/
Macworld (1997-09).dmg
/
Serious Software
/
Cherwell Scientific Demos
/
pro Fit
/
pro Fit 5.0 demo (fpu).sea
/
pro Fit 5.0 demo (fpu)
/
External Modules
/
External modules sources
/
C
/
FFT.c
next >
Wrap
Text File
|
1996-04-28
|
11KB
|
329 lines
/*
* This unit contains the function 'fft' which, together with 350 additional
* programs useful in scientific work, is found in the book 'Numerical Recipes:
* The Art of Scientific Computing', available from Cambridge University Press.
* It is used here by permission of the authors.
*/
/***************************************************************************************/
/* FFT.c */
/* */
/* Version 26.9.94 */
/***************************************************************************************/
#include "proFit_interface.h"
#include <math.h>
static double sqr(double x) {return (x*x);}
static void fft (double* x, int num,char inverse)
{
long ii, jj, n, mmax, m, j, istep, i;
double wtemp, wr, wpr, wpi, wi, theta, tempr, tempi;
n = num;
j = 1;
for (ii = 1;ii<= (n / 2);ii++)
{
i = 2 * ii - 1;
if (j > i)
{
tempr = x[j];
tempi = x[j + 1];
x[j] = x[i];
x[j + 1] = x[i + 1];
x[i] = tempr;
x[i + 1] = tempi;
}
m = n / 2;
while ((m >= 2) && (j > m))
{
j = j - m;
m = m / 2;
}
j = j + m;
if (TestStop()) // test if proFit or a user tries to stop the program }
goto end;
} //for ii = 1 to (n div 2) do }
mmax = 2;
while (n > mmax)
{
istep = 2 * mmax;
theta = 6.283185307959 / mmax;
if (inverse)
theta = -theta;
wpr = -2 * sqr(sin(0.5 * theta));
wpi = sin(theta);
wr = 1;
wi = 0;
for (ii = 1;ii<= (mmax / 2);ii++)
{
m = 2 * ii - 1;
for (jj = 0;jj<= ((n - m) / istep);jj++)
{
i = m + jj * istep;
j = i + mmax;
tempr = wr * x[j] - wi * x[j + 1];
tempi = wr * x[j + 1] + wi * x[j];
x[j] = x[i] - tempr;
x[j + 1] = x[i + 1] - tempi;
x[i] = x[i] + tempr;
x[i + 1] = x[i + 1] + tempi;
} // for jj := 0 to ((n - m) div istep) do }
wtemp = wr;
wr = wr + wr * wpr - wi * wpi;
wi = wi + wi * wpr + wtemp * wpi;
if (TestStop()) //test if proFit or a user tries to stop the program }
goto end;
} // for ii := 1 to (mmax div 2) do }
mmax = istep;
} // while n>mmax }
end:;
} // fft }
static void shiftFFT (double* x, long np)
{
double xx;
long i;
for (i = 1;i<= np;i++)
{
xx = x[i];
x[i] = x[i + np];
x[i + np] = xx;
}
} // shiftFFT }
static void CleanUpRun(double* x)
{ StopExecution();
if (x!=0) {SysBeep(1);DisposePtr((Ptr) x);x=0;}
}
/***************************************************************************************/
void SetUp ( short* const moduleKind, /* set moduleKind to isFunction or isProgram */
Str255 name, /* the name of the program or function (pascal string) */
long* const requiredGlobals, /* the number of bytes to be allocated in ExtModulesParamBlock.globals */
/* set requiredGlobals to 0 if you don't use this feature */
ExtModulesParamBlock* pb) /* the complete parameter block passed by pro Fit to the */
/* routines defined in this file. In most cases it can be ignored */
/* SetUp is called once when the external module is linked to proFit */
{
*moduleKind=isProgram; /* we define a program */
SetPascalStr(name,"\pComplex FFT",255); /* with the name "Complex FFT" */
*requiredGlobals=0; /* we define no globals */
}
/***************************************************************************************/
void InitializeProg (ExtModulesParamBlock* pb)
/* Can be left emtpy if not needed. */
/* called when the external module is linked to proFit after SetUp was called */
/* can be used to inititialize global variables, etc. */
{ pb->v[0] = 0; //use it as boolean value to test for the first time this program runs }
pb->v[1] = 64; //these are the default values for the dialog box asking for dataNr, ColumnNrs
pb->v[2] = 1;
pb->v[3] = 4;
pb->v[4] = 0;
pb->v[5] = 0;
}
/***************************************************************************************/
void Run(ExtModulesParamBlock* pb)
/* pro Fit calls this function when the name of the program is chosen from the */
/* Run Program submenu in the menu Calc */
{ InputRec r;
double ex1=pb->v[1],
ex2=pb->v[2],
ex3=pb->v[3],
ex4=pb->v[4],
ex5=pb->v[5],
x0, xinc, y0, yinc, yoff;
long i, xCol, yCol;
double* x=0; // pointer to the data array }
long nrData; // the number of data (integer power of 2) }
char inverse; // false if normal FFT should be done, true for reverse FFT }
short answer;
if (pb->v[0] == 0)
{ // this is the first time this program runs }
pb->v[0] = 1;
//\p instructs the compiler to produce a pascal string
if (AskBox(&answer,"\pDo you want to get some information on this program in the Results window ?"))
return; // stop button pressed
else
{if (answer == 1) //yes button pressed }
{
Writeln("\pFast Fourier Transform: converts between data.");
Writeln("\pin the 'time domain' and data in the 'frequency domain'");
Writeln("\p");
Writeln("\pThe data in the time domain is arranged in");
Writeln("\pthree columns:");
Writeln("\p 1st column: the time for each data point");
Writeln("\p 2nd column: the real value of each data point");
Writeln("\p 2rd column: the imaginary value of each data point");
Writeln("\pThe data in the time domain is also arranged in");
Writeln("\pthree columns:");
Writeln("\p 1st column: the frequency for each data point");
Writeln("\p 2nd column: the real value of each data point");
Writeln("\p 2rd column: the imaginary of value each data point");
Writeln("\p");
Writeln("\pWhen running the program, you must specify the");
Writeln("\pnumber of data points (between 4 and 4096).");
Writeln("\pThe number of data points should be a power of 2.");
Writeln("\pIf it is not a power of 2, it is rounded to the");
Writeln("\pnext power of 2.");
Writeln("\pIf you run an FFT, set 'First input column'");
Writeln("\pto the time column of the data points in the");
Writeln("\ptime domain, 'First output column' to the");
Writeln("\pfrequency column of the data points in the");
Writeln("\pfrequency domain.");
Writeln("\pIf you run an inverse FFT, set 'First input column'");
Writeln("\pto the frequency column of the data points in the");
Writeln("\pfrequency domain, 'First output column' to the");
Writeln("\ptime column of the data points in the");
Writeln("\ptime domain.");
Writeln("\p'Offset of FFT' defines an additive constant for");
Writeln("\pthe frequency values.");
return;
}
}
}
r[0].x =&ex1;
r[0].s = "\pNumber of data points";
r[1].x = &ex2;
r[1].s = "\p$CFirst input column";
r[2].x = &ex3;
r[2].s = "\p$CFirst output column";
r[3].x = &ex4;
r[3].s = "\pOffset of FFT";
r[4].x = &ex5;
r[4].s = "\p$XInverse FFT";
if (InputBox(5, &r)) return; // if something is not ok we quit
else
{ // if everything is ok we do something
ex1 = fabs(ex1);
if ((ex1 > 4096) || (ex1 < 4)) return;
i = ex1;
nrData = 4;
while (2*nrData<=i) nrData *= 2;
ex2 = fabs(ex2);
if ((ex2 > 97) || (ex2 < 1)) return;
xCol = (ex2);
ex3 = fabs(ex3);
if ((ex3 > 97) || (ex3 < 1)) return;
yCol = ex3;
yoff = ex4;
inverse = (ex5 != 0);
pb->v[1] = nrData; //these are the default values for the dialog box asking for dataNr, ColumnNrs
pb->v[2] = ex2;
pb->v[3] = ex3;
pb->v[4] = ex4;
pb->v[5] = inverse;
x = (double*) NewPtr(sizeof(double)*(2L*nrData+1)); // prepare memory for data
if (x == 0) return;
if (TestData(1,xCol))
{
x0 = GetData(1,xCol);
if (TestData(2,xCol))
xinc =GetData(2,xCol) - x0;
}
for (i = 1;i<= nrData;i++) // read data from window
{
if (TestData(i,xCol + 1)) //test if data value is ok }
x[2 * i - 1] = GetData(i,xCol + 1);
else
x[2 * i - 1] = 0;
if (TestData(i,xCol + 2))
x[2 * i] = GetData(i,xCol + 2);
else
x[2 * i] = 0;
if (TestStop()) {CleanUpRun(x);return;}
}
fft(x, 2 * nrData, inverse); // fft calculations }
yinc = 1 / (xinc*nrData) ;
if (!inverse)
{
shiftFFT(x, nrData);
y0 = yoff - 1 / xinc / 2;
}
else
{
y0 = yoff;
for (i = 1;i<= nrData / 2;i++)
{
x[4 * i - 3] = x[4 * i - 3] / nrData;
x[4 * i - 2] = x[4 * i - 2] / nrData;
x[4 * i - 1] = -x[4 * i - 1] / nrData;
x[4 * i] = -x[4 * i] / nrData;
}
}
if (TestStop() ) {CleanUpRun(x);return;}
SetColName(yCol,"\pfrequency");
SetColName(yCol + 1,"\pFFT real");
SetColName(yCol + 2, "\pFFT imag.");
for (i = 1; i<= nrData;i++) // output of data into window }
{
SetData(i,yCol, y0 + (i - 1.0) * yinc);
SetData(i,yCol + 1, x[2 * i - 1]);
SetData(i,yCol + 2, x[2 * i]);
if (TestStop() ) {CleanUpRun(x);return;}
}
if (x != 0)
{ DisposPtr((Ptr) x);
x=0;
} // we don't need the memory any more }
}
}
/***************************************************************************************/
void CleanUp (ExtModulesParamBlock* pb)
/* called when the function or program is removed from pro Fit's menus */
/* in most cases, this function can be empty */
{
}
/***************************************************************************************/
/* for functions, not used here: */
/***************************************************************************************/
void InitializeFunc (Boolean* const hasDerivatives, Str255 descr1stLine, Str255 descr2ndLine,
short* const numberOfParams, DefaultParamInfo* const a0, ExtModulesParamBlock* pb)
{}
void Func ( double x, ParamArray a, double* const y, ExtModulesParamBlock* pb)
{}
void Derivatives(double x, ParamArray a, ParamArray dyda, ExtModulesParamBlock* pb)
{}
short Check(short paramNo, DefaultParamInfo* const a0, ExtModulesParamBlock* pb)
{return ok;}
void First (ParamArray a, ExtModulesParamBlock* pb)
{}
void Last (ExtModulesParamBlock* pb)
{}